#include <gtest/gtest.h>

// Все тесты в gtests - это отдельные различные объекты,
// то есть никакие изменения локальных данных класса MATest
// в тесте 1 не касаются данных в тесте 2.

// Убедительная просьба тестить errno исключительно
// при помощи этого макроса. При таком подходе,
// мы можем увидеть где упала проверка errno, а где
// именно код ошибки неверный.

#ifdef _TEST_ERRNO
#define ASSERT_ERRNO(err)   \
	do {                      \
		ASSERT_EQ(errno, err);  \
		errno = 0;              \
	} while (0)

#define EXPECT_ERRNO(err)   \
	do {                      \
		EXPECT_EQ(errno, err);  \
		errno = 0;              \
	} while (0)

#else
#define ASSERT_ERRNO(err)   \
	do {} while(0)

#define EXPECT_ERRNO(err)   \
	do {} while(0)

#endif

class MATest : public ::testing::Test {
 protected:
	// этот метод будет вызываться всякий раз при запуске нового теста
	// сразу после конструктора
	virtual void SetUp() {
		// TODO: пока она пустая, но может вы найдете подходящий для неё use-case
	};
	// этот метод будет вызываться при окончании теста точно до деструктора
	virtual void TearDown() {
		// XXX: Если вы не используете классы,
		// то не забывайте после всех тестов писать ma_deinit()
		// А то после первого неудавшегося упадут все последующие,
		// подумав, что мы повторно инитим - у нас же singleton
		ma_deinit();
	}
 private:
	// your data
};

// Теперь классы Rodina* имеют интерфейс аналогичный MAInit
// Такое наследование было проведено, чтобы разграничить test case
// и при этом не дублировать код MATest
class RodinaMaInit : public MATest {};
class RodinaMaAlloc : public MATest {};
class RodinaMaFree : public MATest {};
class RodinaMaGet : public MATest {};
class RodinaMaRelease : public MATest {};
class RodinaGeneralTest : public MATest {};

// TEST_F(ClassTest, TestName) - макрос, позволяющий создавать тесты
// Если вы не планируете использовать классы,
// то стоит использовать TEST(TestCase, TestName)
// Имя теста скадывается из пары class/test case и test_name,
// так что в разных test case'ах могут быть тесты с одинаковыми названиями
// Test case стоит называть таким образом, чтобы было понятно чьи это тесты
// и что конкретно(в нашем случае, какую функцию) автор тестировал

// Опишу буквально парочку самых необходимых макросов для тестирования.
// Остальное смотрите в Primer/AdvancedGoogleTestTutorial'ах:
// https://code.google.com/p/googletest/wiki/Primer
// https://code.google.com/p/googletest/wiki/AdvancedGuide
// EXPECT_*(...), ASSERT_*(...) - основные макросы gtests
// Проверяют истинность выражения в скобках.
// Всегда существуют в паре.
// Отличаются тем, что после неудавшегося EXPECT_* - ваш тест репортит ошибку
// и продолжает выполняться. После неудавшегося ASSERT_* тест завершается и
// дальнейшие проверки не выполняются.
// ASSERT_* может быть полезен при тестировании функций вроде malloc(),
// когда дальнейшее исполнение кода бессмысленно.
// Полезные макросы:
// EXPECT/ASSERT_EQ(actual, expected); act == exp
// EXPECT/ASSERT_NE(expr1, expr2); e1 != e2
// EXPECT/ASSERT_LT(expr1, expr2); e1 < e2
// EXPECT/ASSERT_LE(expr1, expr2); e1 <= e2
// EXPECT/ASSERT_GT(expr1, expr2); e1 > e2
// EXPECT/ASSERT_GE(expr1, expr2); e1 >= e2
// EXPECT/ASSERT_TRUE(expr); проверяет истинность expr
// EXPECT/ASSERT_FALSE(expr); проверяет ложность expr
// На этом число макросов не ограничивается, но мне этого хватило.
// Подробней смотрите в гайдах.

/* ma_init tests */

TEST_F(RodinaMaInit, AlreadyInited) {
	// SetUp() will be called
	EXPECT_EQ(ma_init(100, 200, "mem_swap"), 1);
	EXPECT_EQ(ma_init(100, 200, "mem_swap"), 0);
	EXPECT_ERRNO(EALREADY);
	EXPECT_EQ(ma_init(100, 200, "mem_swap"), 0);
	EXPECT_ERRNO(EALREADY);
	// ma_deinit() will be called by TearDown()
}

/* ma_alloc tests*/

TEST_F(RodinaMaAlloc, ZeroSizeMemBlock) {
	// SetUp() will be called
	ASSERT_EQ(ma_init(100, 200, "mem_swap"), 1); // we don't need to continue
	EXPECT_EQ(ma_alloc(400), 0);
	EXPECT_ERRNO(E2BIG);
	// ma_deinit() will be called by TearDown()
}

// Еще один полезный момент:
// Если какой-то тест у вас постоянно падает,
// а вы пока не знаете как это починить,
// то можно добавить приставку DISABLED_ к имени теста,
// и он будет помечен при запуске, как отключенный.


// TODO: To be continued
